//前言:多态所涉及到的知识点如果是32位平台需要考虑指针是4byte,如果编译器是64位则要考虑指针大小为8byte
#include<iostream>
using namespace std;



//一丶 多态的概念
//1.多态的概念:通俗的来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生不同的状态


//2.多态的种类
//静态的多态:例如函数重载,比如<<  能自己实现一下输出对象,也能输出内置类型
// ----再形象一点就是:模板函数(add),传不同类型的参数实例化出不同的函数,多态的行为可以说是重载完成的      --可以说函数重载才是多态
//-----静态:这个意思是编译的时候


//动态的多态: ----一个父类对象的引用或指针去调用同一个函数,传递不同的对象会调用不同的函数.
//----动态:是运行时实现的 下面是举例


//总的来说多态无论是静态还是动态,都和类型是有关系的







//3.虚函数
//就像下面的这种定义的方式就是虚函数              ----虚函数一般有两种定义的方式,在类的内部定义,    在类的内部声明,在外部定义       并且虚函数只能在类中使用
//class test
//{
//public:
//    virtual int add()
//    {
//        return 2;
//    }
//
//    virtual int sub();
//};
//int test::sub()
//{
//    return 2;
//}

//补充:虚函数只能是类的非静态成员函数     也就是不能这样定义  static virtual int.....






//4.!!!子类函数的重写        涉及到虚函数的使用          也就是重写(覆盖)只会在子类中进行


//class person{
//public:
//    virtual void BuyTicket()        //虚继承和虚函数的定义没有任何关系
//    {
//        cout<<"买票-全价"<<endl;
//    }
//};
//
////!!!!!重写的条件 三同   必须是虚函数,如果不是就会构成隐藏
//class student:public person
//{
//public:
//    virtual void BuyTicket()//之前讲的如果子类和父类的成员函数名字相同则会被隐藏,现在这里就是个例外       这里子类和父类定义的是虚函数
//    //虚函数如果满足三同,就不会被隐藏而是被重写(覆盖)  也就是按照子类的函数为准,继承的父类的同名成员函数责备删除
//    // -----!!!!必须是虚函数  函数名相同   函数的参数相同   函数的返回值相同
//    //如果不是虚函数  或者是虚函数但是三同中有一条没有被满足,就会构成隐藏
//    {
//        cout<<"买票-半价"<<endl;
//    }
//};
//
//void Func(person& p)//父类的引用或者父类的指针均可以直接接受子类地址,或者子类对象        因为会被切片切过去
////void Func(person* p)指针也会构成多态               !!!!切记只有父类的引用或者父类的指针才能构成动态的多态
//{
//    p.BuyTicket();//这就是多态,传进来不同类型的对象,运行出,i不同的结果
//}
//
//
//int main(void)
//{
//    student d1;
//    Func(d1);
//}




//-----协变       多态的例外---返回值类型可以稍微的不同
//补充:重写三同之一要求返回值相同有一个例外:  协变---要求返回值是父子关系的指针或引用
//也就是说返回值为其他类型的不行,也就返回值为  person* student*           person& student&        两个指针任意组合或者两个引用任意组合


//补充:  虚函数-重写
//重写并不是子类父类,同名函数都是虚函数,而是在三同的前提下,两者都是虚函数,或者只有父类是虚函数(也就是子类只是普通函数但不是虚函数)
//但是在实际使用的过程中建议两者都是虚函数
//虽然子类没写virtual,但是他继承了父类虚函数的属性,再完成重写,那么他也算是虚函数
//!!!子类的函数哪怕在private下也能完成重写,也就是说只要父类是公有就可以


//---子类重写的函数可以不加上virtual
//本质上是因为析构函数,父类析构函数加上virtual,那么就不构成多态,没调到子类的析构函数,这个就是个内存泄露的场景








//二丶 多态的定义以及实现
//多态是在不同继承关系的类对象,去调同一函数,产生了不同的行为.比如student继承了person,person对象买票全价,student对象买票半价

//---在继承中构成多态还有两个条件               ----上面虚函数的重写就是一个例子
//其一:必须通过基类的指针或引用来调用这个虚函数
//其二:被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写


//也就是说传什么类型的对象,就会调用哪个类型的虚函数        跟对象有关
//如果没有构成多态,调用的就是p类型的函数,    也就是说和传入的类型有关















//三丶  析构函数的重写
//class person
//{
//public:
//    virtual ~person()//虚构函数可以定义成虚函数          并且两个虚函数可以构成重写
//    {
//        cout<<"~person"<<endl;
//    }
//};
//
//class student:public person
//{
//public:
//    virtual ~student()//析构函数的名字都会被特殊处理,都被处理成了destructor(),也就是构成了多态
//    {
//        cout<<"~student()"<<endl;
//    }
//};

//int main(void)
//{
////    //析构函数无论是不是虚函数,是不是能完成重写,都会正确调用
////    person p1;
////    student s1;
//
//    person* p1=new person;         //申请空间就相当于  operator new   +构造函数
//    person* p2=new student;
//    //如果p2是student*类型的就不需要析构函数为虚函数
//
//    delete p1;           //销毁空间就相当于   p1->destructor()         +operator delete
//    delete p2;      //p2指向的是继承的父类的那个空间,最后析构只回析构父类的空间,但是p2申请的student大小的空间,没有被完全析构
//    //所以在这种情况下就需要在两个类的析构函数的前面加上virtual 就可以构成多态了,也就不会造成内存泄露的问题了
//    //如果析构函数不构成多态,那么p2指针释放的时候就会造成内存泄露,也就是不会析构student子类成员的空间
//}
//总结就是: 动态申请的对象,如果是父类指针接收了子类的地址,此时就需要虚构函数是虚函数         ---可以说就这一种场景,需要析构函数为虚函数












